package com.eol.service.pay;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.eol.common.constants.BaseCommonConstants;
import com.eol.common.exception.BaseException;
import com.eol.common.pay.WxPayBean;
import com.eol.common.sequence.Sequence;
import com.eol.common.utils.StringUtils;
import com.eol.entity.dto.pay.H5SceneInfo;
import com.eol.entity.dto.pay.PayInputDto;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.RsaKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Created with IntelliJ IDEA.
 * User: liulinhui
 * Date: 2020/5/25
 * Time: 2:56 PM
 * Description: PayService
 */
@Service
@Slf4j
public class PayService {

    public static final String WX_PAY_NOTIFY_URL = "/wxPayNotify/";
    public static final String ALI_PAY_NOTIFY_URL = "/aliPayNotify/";

    @Value("${base.webUrl:''}")
    private String BaseUrl;
    @Value("${spring.profiles.active:'prod'}")
    private String profiles;
    @Resource
    private WxPayApiConfig wxPayApiConfig;
    @Resource
    private AliPayApiConfig aliPayApiConfig;

    /**
     * 支付
     *
     * @param payDto 支付参数
     * @return 支付参数
     */
    public String pay(PayInputDto payDto) throws AlipayApiException {
        switch (payDto.getChannelType()) {
            case BaseCommonConstants.PAY_CHANNEL_WX_JSAPI:
                return wxJsapiPay(payDto);
            case BaseCommonConstants.PAY_CHANNEL_WX_MWEB:
                return wxH5Pay(payDto);
            case BaseCommonConstants.PAY_CHANNEL_ALIPAY_WAP:
                return aliMobilePay(payDto);
            default:
                return "";
        }
    }

    /**
     * 微信公众号支付
     */
    public String wxJsapiPay(PayInputDto payDto) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = StringUtils.getIp(request);
        WxPayApiConfigKit.setThreadLocalWxPayApiConfig(wxPayApiConfig);
        WxPayApiConfig config = WxPayApiConfigKit.getWxPayApiConfig();
        Map<String, String> params;
        if (profiles.contains(BaseCommonConstants.PROD)) {
            params = UnifiedOrderModel
                    .builder()
                    .appid(config.getAppId())
                    .mch_id(config.getMchId())
                    .sub_appid(config.getSlAppId())
                    .sub_mch_id(config.getSlMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payDto.getDes())
                    .total_fee(payDto.getMoney().multiply(new BigDecimal(100)).toBigInteger().toString()) //单位为分
                    .spbill_create_ip(ip)
                    .notify_url(BaseUrl + WX_PAY_NOTIFY_URL + String.format("%s/%s/%s",
                            payDto.getOrderId(), payDto.getChannelType(), payDto.getType()))
                    .trade_type(payDto.getChannelType())
                    .openid(payDto.getOpenId())
                    .out_trade_no(payDto.getOrderId())
                    .build()
                    .createSign(config.getPartnerKey(), SignType.HMACSHA256);
        } else {
            params = UnifiedOrderModel
                    .builder()
                    .appid(config.getAppId())
                    .mch_id(config.getMchId())
                    .sub_appid(config.getSlAppId())
                    .sub_mch_id(config.getSlMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payDto.getDes())
                    .total_fee(new BigDecimal(BaseCommonConstants.TEST_PAY_AMOUNT).multiply(new BigDecimal(100)).toBigInteger().toString())
                    .spbill_create_ip(ip)
                    .notify_url(BaseUrl + WX_PAY_NOTIFY_URL + String.format("%s/%s/%s",
                            payDto.getOrderId(), payDto.getChannelType(), payDto.getType()))
                    .trade_type(payDto.getChannelType())
                    .out_trade_no(payDto.getOrderId())
                    .openid(payDto.getOpenId())
                    .build()
                    .createSign(config.getPartnerKey(), SignType.HMACSHA256);
        }
        log.info("构造微信公众号支付请求参数：" + params.toString());
        String xmlResult = WxPayApi.pushOrder(false, params);
        log.info("构造微信公众号支付请求结果：" + xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            throw new BaseException(returnMsg);
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            throw new BaseException(returnMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");
        Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        String jsonStr = JSON.toJSONString(packageParams);
        log.info("微信公众号支付支付返回的参数:" + jsonStr);
        return jsonStr;
    }


    /**
     * 微信h5支付
     *
     * @return 返回跳转链接
     */
    public String wxH5Pay(PayInputDto payInputDto) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = StringUtils.getIp(request);
        H5SceneInfo sceneInfo = new H5SceneInfo();

        H5SceneInfo.H5 h5_info = new H5SceneInfo.H5();
        h5_info.setType("Wap");
        //此域名必须在商户平台--"产品中心"--"开发配置"中添加
        h5_info.setWap_url("");
        h5_info.setWap_name(payInputDto.getDes());
        sceneInfo.setH5_info(h5_info);
        WxPayApiConfigKit.setThreadLocalWxPayApiConfig(wxPayApiConfig);
        WxPayApiConfig config = WxPayApiConfigKit.getWxPayApiConfig();
        Map<String, String> params;
        if (profiles.contains(BaseCommonConstants.PROD)) {
            params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .sub_appid(config.getSlAppId())
                    .sub_mch_id(config.getSlMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payInputDto.getDes())
                    .attach(payInputDto.getDes())
                    .out_trade_no(payInputDto.getOrderId())
                    .total_fee(payInputDto.getMoney().multiply(new BigDecimal(100)).toBigInteger().toString()) //单位为分
                    .spbill_create_ip(ip)
                    .notify_url(BaseUrl + WX_PAY_NOTIFY_URL + String.format("%s/%s/%s",
                            payInputDto.getOrderId(), payInputDto.getChannelType(), payInputDto.getType()))
                    .trade_type(TradeType.MWEB.getTradeType())
                    .scene_info(JSON.toJSONString(sceneInfo))
                    .build()
                    .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        } else {
            params = UnifiedOrderModel
                    .builder()
                    .appid(wxPayApiConfig.getAppId())
                    .mch_id(wxPayApiConfig.getMchId())
                    .sub_appid(config.getSlAppId())
                    .sub_mch_id(config.getSlMchId())
                    .nonce_str(WxPayKit.generateStr())
                    .body(payInputDto.getDes())
                    .attach(payInputDto.getDes())
                    .out_trade_no(payInputDto.getOrderId())
                    .total_fee(new BigDecimal(BaseCommonConstants.TEST_PAY_AMOUNT).multiply(new BigDecimal(100)).toBigInteger().toString())
                    .spbill_create_ip(ip)
                    .notify_url(BaseUrl + WX_PAY_NOTIFY_URL + String.format("%s/%s/%s",
                            payInputDto.getOrderId(), payInputDto.getChannelType(), payInputDto.getType()))
                    .trade_type(TradeType.MWEB.getTradeType())
                    .scene_info(JSON.toJSONString(sceneInfo))
                    .build()
                    .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        }
        log.info("构造微信h5支付请求参数：" + params.toString());
        String xmlResult = WxPayApi.pushOrder(false, params);
        log.info("构造微信h5支付请求结果：" + xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String return_code = result.get("return_code");
        String return_msg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(return_code)) {
            throw new RuntimeException(return_msg);
        }
        String result_code = result.get("result_code");
        if (!WxPayKit.codeIsOk(result_code)) {
            throw new RuntimeException(return_msg);
        }
        // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
        String prepayId = result.get("prepay_id");
        String webUrl = result.get("mweb_url");
        log.info("prepay_id:" + prepayId + " mweb_url:" + webUrl);
        log.info("微信h5支付返回的参数:" + webUrl);
        return webUrl;
    }

    /**
     * 支付宝h5支付
     *
     * @return 返回跳转链接
     */
    public String aliMobilePay(PayInputDto payInputDto) throws AlipayApiException {
        AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("app_id", aliPayApiConfig.getAppId());
        paramsMap.put("method", "alipay.trade.wap.pay");
        paramsMap.put("return_url", payInputDto.getReturnUrl());
        paramsMap.put("charset", aliPayApiConfig.getCharset());
        paramsMap.put("sign_type", aliPayApiConfig.getSignType());
        paramsMap.put("timestamp", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        paramsMap.put("version", "1.0");
        paramsMap.put("notify_url", BaseUrl + ALI_PAY_NOTIFY_URL + String.format("%s/%s/%s",
                payInputDto.getOrderId(), payInputDto.getChannelType(), payInputDto.getType()));

        Map<String, String> bizMap = new HashMap<>();
        bizMap.put("body", payInputDto.getDes());
        bizMap.put("subject", payInputDto.getDes());
        bizMap.put("out_trade_no", payInputDto.getOrderId());
        bizMap.put("total_amount", payInputDto.getMoney().toPlainString());
        bizMap.put("product_code", "QUICK_WAP_WAY");
        //非生产环境默认支付2分钱
        if (profiles.contains(BaseCommonConstants.PROD)) {
            bizMap.put("total_amount", payInputDto.getMoney().toPlainString());
        } else {
            bizMap.put("total_amount", BaseCommonConstants.TEST_PAY_AMOUNT);
        }

        paramsMap.put("biz_content", JSON.toJSONString(bizMap));

        String content = PayKit.createLinkString(paramsMap);

        System.out.println(content);

        String encrypt = AlipaySignature.rsaSign(content, aliPayApiConfig.getPrivateKey(), "UTF-8", "RSA2");
        System.out.println(encrypt);
        paramsMap.put("sign", encrypt);
        return aliPayApiConfig.getServiceUrl() + "?" + PayKit.createLinkString(paramsMap, true);
    }

}
